package tp1;



import java.io.IOException;
import java.net.*;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Scanner;

// Classe serveur principale
public final class Serveur {

    public static final String ANSI_RESET = "\u001B[0m";
    public static final String ANSI_RED = "\u001B[31m";
    public static final String ANSI_GREEN = "\u001B[32m";
    public static HashSet<Utilisateur> utilisateurs = new HashSet<Utilisateur>();
    public static HashSet<Groupe> groupes = new HashSet<Groupe>();
    private static volatile Serveur instance = null;
    public static int multicastPort = 7654; // Premier port multicast pour les groupes
    private static final int portServeur = 1027; //port pour la connexion TCP entre serveur et client
    private static final int portAnnonceUDP = 7650; //port pour annoncer un serveur
    public static final String adresseMulticast="224.5.6.7"; // adresse multicast pour la communication en groupe
    public static InetAddress adresseMachine=null; // adresse IP de la machine
    private static boolean modeDebug;

    /**
     * Méthode permettant de renvoyer une instance de la classe Serveur
     * @return Retourne l'instance du singleton.
     */
    public final static Serveur getInstance() {
        if (Serveur.instance == null) {
            synchronized(Serveur.class) {
                if (Serveur.instance == null) {
                    Serveur.instance = new Serveur();
                }
            }
        }
        return Serveur.instance;
    }


    private static InetAddress getAdresseLocale(Scanner clavier) throws IOException {

        Enumeration<NetworkInterface> enumReseau = null;
        enumReseau = NetworkInterface.getNetworkInterfaces();
        NetworkInterface interfaceReseau = null;
        InetAddress adresseMachine = null;
        debug("****Liste des Adresses réseau ****\n\n");

        for (NetworkInterface nint : Collections.list(enumReseau)) {

            debug("Nom : "+ nint.getDisplayName()+"\t Adresse Loopback ? "+nint.isLoopback());
            //System.out.println(nint.getName());
            //  System.out.println(nint.isLoopback());
            //  System.out.println(nint.isPointToPoint());
            debug("\nAdresses réseau :");
            Enumeration<InetAddress> inetAddresses = nint.getInetAddresses();
            for (InetAddress inetAddress : Collections.list(inetAddresses)) {
                debug("Adresse : "+inetAddress.getCanonicalHostName()+"\t On site ? "+inetAddress.isSiteLocalAddress());
                if(inetAddress.isSiteLocalAddress() && !inetAddress.isLoopbackAddress())
                    adresseMachine = InetAddress.getByName(inetAddress.getHostName());
            }
        }

        System.out.println(ANSI_GREEN+"Adresse machine choisie = "+adresseMachine.getHostAddress()+ANSI_RESET);

        if(modeDebug) {
            System.out.println("Appuyez sue ENTREE pour continuer ou saisissez une adresse réseau différente");
            String nouvelleAdresse = clavier.nextLine();

            if (nouvelleAdresse.length() > 0) {
                try {
                    InetAddress address = InetAddress.getByName(nouvelleAdresse);
                    if (address.isReachable(200))
                        adresseMachine = address;
                    else
                        System.out.println(ANSI_RED + "ERREUR:" + ANSI_RESET + "Impossible de se connecter à l'adresse " + address.toString());
                } catch (UnknownHostException e) {
                    System.out.println("Impossible de se connecter à l'adresse spécifiée. ENTREE pour continuer.");
                    clavier.nextLine();
                }
            }
        }
        return(adresseMachine);
    }

    protected static void debug(String message) {
        String couleur = ANSI_RED;
        if(modeDebug) {
            System.out.println(couleur+"[Message Debug] "+message+ANSI_RESET);
        }
    }

    public static void main(String[] args) throws UnknownHostException {
		ServerSocket s_socket=null;
        Scanner cmd = new Scanner(System.in);

        if(args.length>0) {
            for(int i=0;i<args.length;i++) {
                System.out.println("Arg[" + i + "] = " + args[i]);
                if (args[i].equals("-d") || args[i].equals("--debug")) {
                    modeDebug = true;
                    System.out.println(ANSI_RED+"================Mode Debug========================="+ANSI_RESET);
                }
            }
        }
        
        try {
			s_socket = new ServerSocket(portServeur);
            adresseMachine = getAdresseLocale(cmd);
		} catch (IOException e) {
			e.printStackTrace();
            System.out.println("Erreur : Impossible de trouver l'adresse réseau de la machine.");
            System.exit(1);
		}
        System.out.println("A l'écoute de clients à l'adresse " +ANSI_RED+adresseMachine.getHostAddress()+ANSI_RESET+" sur le port " +ANSI_RED+ portServeur+ANSI_RESET);

        Thread t2 = null;
        try {
            t2 = new ServeurAnnonce(portAnnonceUDP);
        } catch (IOException e) {
            e.printStackTrace();e.printStackTrace();
        }
        t2.start();

        while (true) {
			try {
				// Accepter les connexions entrantes.
				Socket c_socket = s_socket.accept();
				
				Thread t1 = new ServeurThread(c_socket);
				t1.start();

            } catch (IOException io_Exception) {
				System.out.println("Exception lors de la phase d'accept(). Voici l'erreur :");
				io_Exception.printStackTrace();
				try {
					s_socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
    }


}
